﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OmegaBot;

namespace OmegaBot.Extensions
{
	public static class Extensions
	{
		public enum Faction { Alliance, Horde, Unknown }

		/// <summary>
		/// Returns faction group (Alliance || Horde)
		/// </summary>
		public static Faction PlayerFaction(this GamePlayer player)
		{
			switch (player.PlayerRace)
			{
				case PlayerRace.Human:
				case PlayerRace.Dwarf:
				case PlayerRace.Gnome:
				case PlayerRace.Nightelf:
				case PlayerRace.Draenei:
					return Faction.Alliance;
				case PlayerRace.Orc:
				case PlayerRace.Undead:
				case PlayerRace.Tauren:
				case PlayerRace.Troll:
				case PlayerRace.Bloodelf:
					return Faction.Horde;
			}
			return Faction.Unknown;
		}


		public static Location ProjectAhead(this Location location, double distance)
		{
			Location vector = new Location(location.X - MyPlayer.Me.Location.X, location.Y - MyPlayer.Me.Location.Y, location.Z - MyPlayer.Me.Location.Z);
			double length = Math.Sqrt(Math.Pow(vector.X, 2) + Math.Pow(vector.Y, 2) + Math.Pow(vector.Z, 2));
			float x = (float)(vector.X * distance / length + location.X);
			float y = (float)(vector.Y * distance / length + location.Y);
			float z = (float)(vector.Z * distance / length + location.Z);
			return new Location(x, y, z);
		}

		public static IEnumerable<GameUnit> AllMobs
		{
			get
			{
				foreach (GameObject o in GameObject.GetAllObjects())
					if (o.GameObjectType == BaseObjectType.Mob || o.GameObjectType == BaseObjectType.Player)
						yield return o as GameUnit;
			}
		}

		/// <summary>
		/// Returns the lootables at this unit's location.  May optionally return only non-aggroable 'safe' lootables.
		/// </summary>
		public static IEnumerable<GameUnit> Lootables(this GameUnit thisMob, double distance, bool onlySafe)
		{
			var lootables = AllMobs.Where(mob => mob.ToString().Contains("Lootable=True") && mob.Location.GetDistanceTo(thisMob.Location) <= distance);
			if (onlySafe)
			{
				lootables = lootables.Except(lootables.Where(loot => loot.Hostiles(20).Any()));
			}
			return lootables;
		}

		/// <summary>
		/// Returns the grindables at this unit's location. 
		/// </summary>
		public static IEnumerable<GameUnit> Grindables(this GameUnit thisMob, double distance, int minLevel, int maxLevel)
		{
			minLevel = Math.Min(minLevel, maxLevel);
			maxLevel = Math.Max(minLevel, maxLevel);
			return AllMobs.Where(mob => !mob.IsInCombat && mob.Level >= minLevel && mob.Level <= maxLevel  && mob.CreatureType != CreatureType.Critter && !mob.IsDead && mob.Location.GetDistanceTo(thisMob.Location) <= distance && (mob.Reaction == Reaction.Neutral || mob.Reaction == Reaction.Hostile));
		}

		public static IEnumerable<GameUnit> Grindables(this GameUnit thisMob, double distance)
		{
			return thisMob.Grindables(distance, thisMob.Level - 6, thisMob.Level + 4);
		}

		/// <summary>
		/// Returns the hostiles at this unit's location (not in combat, and not dead).  
		/// </summary>
		public static IEnumerable<GameUnit> Hostiles(this GameUnit thisMob, double distance)
		{
			return thisMob.Location.Hostiles(distance);
		}

		/// <summary>
		/// Returns the hostiles at this location (not in combat, and not dead).
		/// </summary>
		public static IEnumerable<GameUnit> Hostiles(this Location location, double distance)
		{
			return AllMobs.Where(mob => !mob.IsInCombat && !mob.IsDead && location.GetDistanceTo(mob.Location) <= distance && mob.Reaction == Reaction.Hostile);
		}



		/// <summary>
		/// Returns all units attacking this unit, both directly and indirectly.
		/// </summary>
		public static IEnumerable<GameUnit> Attackers(this GameUnit thisMob)
		{
			var totems = AllMobs.Where(mob => mob.CreatureType == CreatureType.Totem && mob.Reaction == Reaction.Friendly);
			List<GameUnit> party = totems.ToList();
			if (thisMob.HasLivePet)
				party.Add(thisMob.Pet);
			party.Add(thisMob);

			return AllMobs.Where(mob => mob.IsInCombat && !mob.IsDead && party.Any(p => mob.TargetGUID == p.GUID));
		}
	}
}
